#include "op-list.h"
#include "sym.h"

static int total_var,total_space;

static inline void set_0(active_status x,uLL pos){
    int t=pos/64;pos%=64;
    uLL mask=~(1uLL<<pos);
    x[t]&=mask;
    return;
}

static inline void set_1(active_status x,uLL pos){
    int t=pos/64;pos%=64;
    x[t]|=1uLL<<pos;
    return;
}

static inline void merge(active_status x,const active_status y){
    if(!y) return;
    for(int i=0;i<total_space/sizeof(uLL);i++) x[i]|=y[i];
    return;
}

static inline active_status deep_copy(const active_status x){
    static uLL ret[64];
    memcpy(ret,x,total_space);
    return ret;
}

static inline int cmp(const active_status x,const active_status y){return memcmp(x,y,total_space)!=0;}

static inline void assign_space(Operand x){
    if(x->kind==VARIABLE&&x->offset==-1) x->offset=++total_var;
    return;
}

static InterCodes * var_count(InterCodes * start){
    total_var=total_space=0;
    InterCodes * end;
    for(start=start->next;start&&start->code.kind!=FUNC;start=start->next)if(start->run){
        InterCode inst=start->code;
        int u=inst.kind;
        Operand x=inst.x,y=inst.y,z=inst.z;
        switch(u){
            case ADD:case SUB:case MUL:case OP_DIV:
                assign_space(z);
            case ASSIGN:case GTADDR:case TOADDR:case ASSADDR:case OP_IF: 
                assign_space(y);
            case RET:case CALL:case READ:case WRITE:case ARG:case PARAM:
                assign_space(x);break;
            case DEC:
                x->offset=-120; break;
            default: break;
        }
        end=start;
    }
    total_space=(total_var+63)/64*sizeof(uLL);
    return end;
}

static void staticRunChk(InterCodes * now){
    Assert(now,"should not be NULL!");
    if(now->run) return;
    now->run=1;
    int u=now->code.kind;
    if(u==GOTO){
        now->code.x->offset=1;
        staticRunChk(now->code.x->entry);
        return;
    }
    if(u==OP_IF) now->code.z->offset=1,staticRunChk(now->code.z->entry);
    if(u==RET) return;
    if(u==CALL) staticRunChk(now->code.y->entry);
    if(now->next) staticRunChk(now->next);
    return; 
}

static int active_run(InterCodes * now){
    InterCode inst=now->code;
    int u=inst.kind;
    Operand x=inst.x,y=inst.y,z=inst.z;
    if(now->in==NULL){
        now->in=malloc(total_space);
        memset(now->in,0,total_space);
        now->out=malloc(total_space);
        memset(now->out,0,total_space);
    }
    InterCodes * nxt=now->next;
    int ret=0;
    if(u!=RET){
        if(u==GOTO) nxt=x->entry;
        active_status prev=deep_copy(now->out);
        if(nxt->in) memcpy(now->out,nxt->in,total_space);
        if(u==OP_IF) merge(now->out,z->entry->in);
        ret=cmp(prev,now->out);
    }
    memcpy(now->in,now->out,total_space);
    if(u==LABL||u==FUNC||u==GOTO||u==DEC||u==PARAM) 
        return ret;
    if(u==ASSADDR||u==OP_IF||u==RET||u==ARG||u==WRITE){
        if(x->offset>0) set_1(now->in,x->offset-1);
    }else set_0(now->in,x->offset-1);
    if(u==RET||u==ARG||u==CALL||u==PARAM||u==READ||u==WRITE) 
        return ret;
    if(y->offset>0) set_1(now->in,y->offset-1);
    if(u==ADD||u==SUB||u==MUL||u==OP_DIV) 
        if(z->offset>0) set_1(now->in,z->offset-1);
    return ret;
}

void active_var_analyse(InterCodes * first){
    staticRunChk(sym_find("main")->xx->entry);
    for(InterCodes * end;first;first=end->next){
        while(first&&!first->run) first=first->next;
        if(!first) break;
        end=var_count(first);
        first->code.x->offset=total_var;
        int t=1;
        while(t){
            t=0;
            for(InterCodes * now=end;now!=first;now=now->prev)
                if(now->run) t|=active_run(now);
        }
    }
}
